嵌入式开发

您所在的位置:网站首页 stm32 can接收缓存清除 嵌入式开发

嵌入式开发

2023-08-31 02:46| 来源: 网络整理| 查看: 265

回顾

之前讲过用 利用IDLE空闲中断来接收不定长数据 ,但是没有用到DMA,其实用DMA会更加的高效,MCU也可以腾出更多的性能去处理应该做的事情。

原理简介

IDLE顾名思义,就是空闲的意思,即当监测到串口空闲超过1个串口的数据帧时,会使状态寄存器(SR或ISR)的IDLE位置位,如果此时控制寄存器(CR或CR1)的IDLEIE为1,则会触发IDLE中断。

DMA搬运数据,则是一边接收数据,一边将串口接收到的数据搬运到内存中,这个过程不需要MCU参与,等到IDLE中断到来的时候,直接去内存中取数据即可。 DMA中断在CubeMX中是默认开启的,可以手动将其关闭,等IDLE中断到来的时候,直接操作读取数据即可。当DMA设置为NORMAL模式时,这个中断是完全用不到的,因为当IDLE到来时,置标志位,然后数据处理,此时需要重启DMA,而DMA的接收缓冲区又大于不定长的数据帧,这样DMA中断就永远不会触发了。

知道原理了,就好操作了。

CubeMX操作

开启调试接口 在这里插入图片描述

开启USART1异步方式,波特率、数据长度和校验方式根据需要设置 在这里插入图片描述 开启DMA,内存自增,循环方式 在这里插入图片描述 开启串口中断 在这里插入图片描述 设置好时钟 在这里插入图片描述 然后是 Generate Code生成代码

手动修改代码

中断函数中,置标志位,读取SR和DR寄存器,以清除IDLE中断标识,并获得本次接收的数据长度

//485发送数据 //由于485发送数据时,接收端会同步接收到发送的数据,这会造成数据解析的错乱。 //所在在485换向之前,先关闭DMA,等发送完成,再转换到输出状态以后,再开启DMA //也就是说,作为从机来说,不应当主动发起数据传输请求,否则当主机开始发送数据时,从机会丢失数据帧 void LL_myuart_send(u8 *pdata, u16 len) { HAL_UART_DMAStop(&huart1); //先关闭DMA RS485_OUT(); HAL_Delay(1); HAL_UART_Transmit(&huart1, pdata, len, 1000); RS485_IN(); HAL_Delay(1); rs485_receive_pos = 0; rs485_receive_len = 0; HAL_UART_Receive_DMA(&huart1,rs485_receive_data, RS485_BUF_LEN); //等发送完成,再转换到输出状态以后,再开启DMA } void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ u8 temp; /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ //长时间未接收到数据时,会发生IDLE中断,此时意味着数据接收完成 //不同的内核,清除IDLEIE的方式不同,请查阅手册 if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) { //读SR和DR寄存器,以清除IDLE和RXNE中断标志 temp = huart1.Instance->SR; temp = huart1.Instance->DR; rs485_idle_flag = 1; rs485_receive_pos += rs485_receive_len; if(rs485_receive_pos >= RS485_BUF_LEN) //如果起始位置超出缓冲区,则减去缓冲区长度,也就意味着数据从头开始循环记录 rs485_receive_pos -= RS485_BUF_LEN; //DMA接收串口数据, //设置为NORMAL方式时,需要在每一次接收完成后,再次使能,否则DMA的接收缓冲区满了以后就不会再接收 //设置为CIRCLAR方式时,不需要再次使能,但编程会麻烦些 //如果长度大于缓冲区长度 if(RS485_BUF_LEN rs485_idle_flag = 0; //HAL_UART_Receive_DMA(&huart1,rs485_receive_data,RS485_BUF_LEN); //如果DMA采用NORMAL方式,需要激活本句,以使能DMA LL_myuart_send((u8*)"receive OK\r\n", 12); rs485_idle_flag = 0; } /* USER CODE END WHILE */ } 实测

在这里插入图片描述 在这里插入图片描述

下图为缓冲区写满时,从头开始循环写入的情况 在这里插入图片描述 在主程序中,从rs485_receive_pos开始,读取rs485_receive_len长度的数据即可,如果超出缓冲区,则减去缓冲区长度,从头开始读取,不再列出代码。

注意事项 1 DMA接收串口数据

设置为NORMAL方式时,需要在每一次接收完成后,再次使能,否则DMA只接收一次就不会再接收 设置为CIRCLAR方式时,不需要再次使能,但可能会由于各种延时导致数据来不及处理的问题

2 需要开启串口的总中断

在CubeMX中需要开启串口的总中断,否则会不进中断。

3 清中断标识

对于F1系列来说,清IDLE中断标志需要读取SR和DR寄存器,否则会一直进IDLE中断。 不同的内核,清IDLE标志的方法不同,这个需要查询芯片手册

4 非ST家的芯片,本代码不兼容

亲测航顺芯片就不兼容,需要改代码。

5 DMA操作

DMA的计数是连续的,每次接收都是在上次接收完成的位置,继续进行下一次接收,缓冲区装满时,继续从头开始接收,这给编程带来一些麻烦。

还有一种方式简单粗暴:首先接收缓冲区的长度 > 2倍的最大帧长度,然后在每次接收完成后,停止并重新开启DMA,这个过程可以放在主程序中,处理数据时进行。这样每次数据都是从缓冲区的0偏移开始接收数据,所以编程时缓冲区数据不会循环写入,编程上有便利,如果数据传输量不大,而且传输间隔也比较长,可以用这种方式。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3